#include "Image.h"
#include <stdio.h>
#include <string.h>

Image::Image(): width(0), height(0), data(0)
{
	segment.SetImage(this);
}

Image::Image(unsigned int newWidth, unsigned int newHeight): width(0), height(0), data(0)
{
	segment.SetImage(this);
	SetDimension(newWidth, newHeight);
}

Image::~Image()
{
	Clear();
}

Image& Image::operator=(const Image& rhs)
{
	SetDimension(rhs.width, rhs.height);

	if (rhs.data)
	{
		memcpy(data, rhs.data, rhs.Size());
	}

	return *this;
}


void Image::SetDimension(unsigned int newWidth, unsigned newHeight)
{
	unsigned int newSize = newWidth * newHeight;
	unsigned int curSize = width * height;

	if (newWidth == 0 || newHeight == 0)
		throw "Image: Cannot set invalid dimension";

	width = newWidth;
	height = newHeight;

	if (newSize != curSize)
	{
		ClearImage();

		data = new unsigned char[width * height];
	}
}

void Image::Clear()
{
	ClearImage();

	segment.ClearImage();
}

void Image::ClearImage()
{
	if (data)
	{
		delete[] data;
		data = 0;
	}
}


void Image::ReadFromFile(const char* filename)
{
	ReadRawFromFile(filename);
}

void Image::ReadRawFromFile(const char* filename)
{
	if (data == 0)
		throw "Image: No data";

	FILE *f = fopen(filename, "rb");

	if(f == 0)
		throw "Image: Could not read file";

	unsigned int filesize;

	fseek(f, 0, SEEK_END);
	filesize = ftell(f);
	fseek(f, 0, SEEK_SET);


	if (Size() != filesize)
		throw "Image: Invalid dimension";

	
	fread(data, sizeof(unsigned char), Size(), f);

	fclose(f);
}

void Image::ReadRawFromData(const void *imagedata, unsigned int size)
{
	if (data == 0)
		throw "Image: No data";

	if (size != Size())
		throw "Image: Invalid dimension";

	memcpy(data, imagedata, Size());
}

void Image::ReadRawFromStream(ByteStream& stream)
{
	if (stream.Read(data, Size()) != Size())
		throw "Image: Read error";
}


void Image::WriteToFile(const char* filename)
{
	WriteRawToFile(filename);
}

void Image::WriteRawToFile(const char* filename)
{
	if (data == 0)
		throw "Image: No data";

	FILE *f = fopen(filename, "wb");

	if(f == 0)
		throw "Image: Could not write to file";

	fwrite(data, sizeof(unsigned char), Size(), f);

	fclose(f);
}

void Image::ReadRawFromSegment(unsigned int x0, unsigned int y0, const ImageSegment& seg)
{
	ImageSegment *localseg = SegmentFromOffset(x0, y0, seg.Width(), seg.Height());
	if (!localseg)
		throw "Image: Invalid segment";

	localseg->ReadRawFromSegment(seg);
}

void Image::SetComponent(unsigned int x, unsigned int y, unsigned char intensity)
{
	if (!data || x >= width || y >= height)
		throw "Image: SetComponent out of bounds";

	data[x+y*width] = intensity;
}

unsigned char Image::GetComponent(unsigned int x, unsigned int y) const
{
	if (!data || x >= width || y >= height)
		throw "Image: GetComponent out of bounds";

	return data[x+y*width];
}

unsigned char& Image::operator()(unsigned int x, unsigned int y)
{
	if (!data || x >= width || y >= height)
		throw "Image: operator() out of bounds";

	return data[x+y*width];
}


void Image::InitializeSegment(unsigned int blockWidth, unsigned int blockHeight)
{
	segment.SetDimension(blockWidth, blockHeight);

	segment.SetOriginX(-blockWidth);
	segment.SetOriginY(0);
}

ImageSegment* Image::SegmentFromOffset(unsigned int x0, unsigned int y0, unsigned int blockWidth, unsigned int blockHeight)
{
	segment.SetDimension(blockWidth, blockHeight);

	segment.SetOriginX(x0);
	segment.SetOriginY(y0);

	return &segment;
}

ImageSegment* Image::NextSegment()
{
	ImageSegment *result = 0;


	segment.IncrementOriginX( segment.Width() );
	if (segment.OriginX() >= width)
	{
		segment.SetOriginX(0);
		segment.IncrementOriginY( segment.Height() );
	}

	if (segment.OriginY() >= height)
	{
		segment.SetOriginX( width );
		segment.SetOriginY( height );

		result = 0;
	}
	else
	{
		result = &segment;
	}

	return result;
}
